#pragma once

#include <iostream>
#include <unistd.h>
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include "Thread.hpp"
#include "Log.hpp"
#include "LockGuard.hpp"

using namespace ThreadMoudle;
using namespace log_ns;

static const int gdefaultnum = 5;

void test()
{
    while (true)
    {
        std::cout << "hello world" << std::endl;
        sleep(1);
    }
}

template <typename T>
class ThreadPool
{
private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void Wakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void WakeupAll()
    {
        pthread_cond_broadcast(&_cond);
    }
    void Sleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    bool IsEmpty()
    {
        return _task_queue.empty();
    }
    void HandlerTask(const std::string &name) // this
    {
        while (true)
        {
            // 取任务
            LockQueue();
            while (IsEmpty() && _isrunning)
            {
                _sleep_thread_num++;
                LOG(INFO, "%s thread sleep begin!\n", name.c_str());
                Sleep();
                LOG(INFO, "%s thread wakeup!\n", name.c_str());
                _sleep_thread_num--;
            }
            // 如果在任务队列为空且线程池已经停止的情况下，线程会释放锁并退出。这个条件保证了当线程池关闭时，工作线程能够优雅地停止。
            // 判定一种情况
            if (IsEmpty() && !_isrunning)
            {
                UnlockQueue();
                LOG(INFO, "%s thread quit\n", name.c_str());
                break;
            }

            // 有任务
            T t = _task_queue.front();
            _task_queue.pop();
            UnlockQueue();

            // 处理任务
            t(); // 处理任务，此处不用/不能在临界区中处理
            // std::cout << name << ": " << t.result() << std::endl;
            // LOG(DEBUG, "hander task done, task is : %s\n", t.result().c_str());
        }
    }
    void Init()
    {
        func_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);
        for (int i = 0; i < _thread_num; i++)
        {
            std::string threadname = "thread-" + std::to_string(i + 1);
            _threads.emplace_back(threadname, func);
            LOG(DEBUG, "construct thread %s done, init success\n", threadname.c_str());
        }
    }
    void Start()
    {
        _isrunning = true;
        for (auto &thread : _threads)
        {
            LOG(DEBUG, "start thread %s done.\n", thread.Name().c_str());
            thread.Start();
        }
    }
    ThreadPool(int thread_num = gdefaultnum)
        : _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    ThreadPool(const ThreadPool<T> &) = delete;
    void operator=(const ThreadPool<T> &) = delete;

public:
    void Stop()
    {
        LockQueue();
        _isrunning = false;
        WakeupAll();
        UnlockQueue();
        LOG(INFO, "Thread Pool Stop Success!\n");
    }

    // 如果是多线程获取单例呢？
    static ThreadPool<T> *GetInstance()
    {
        if (_tp == nullptr)
        {
            LockGuard lockguard(&_sig_mutex);
            if (_tp == nullptr)
            {
                LOG(INFO, "create threadpool\n");
                // thread-1 thread-2 thread-3....
                _tp = new ThreadPool<T>();
                _tp->Init();
                _tp->Start();
            }
            else
            {
                LOG(INFO, "get threadpool\n");
            }
        }
        return _tp;
    }

    void Equeue(const T &in)
    {
        LockQueue();
        if (_isrunning)
        {
            _task_queue.push(in);
            if (_sleep_thread_num > 0)
                Wakeup();
        }
        UnlockQueue();
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    int _thread_num;
    std::vector<Thread> _threads;
    std::queue<T> _task_queue;
    bool _isrunning;

    int _sleep_thread_num;

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    // 单例模式
    // volatile static ThreadPool<T> *_tp;
    static ThreadPool<T> *_tp;
    static pthread_mutex_t _sig_mutex;
};

template <typename T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;
template <typename T>
pthread_mutex_t ThreadPool<T>::_sig_mutex = PTHREAD_MUTEX_INITIALIZER;

// 这段代码实现了一个线程池（ThreadPool）类，它能够管理一组工作线程，并使得这些线程从任务队列中获取任务并执行。任务可以是任何类型的可调用对象（例如 std::function<void()>）。该类还使用了 单例模式 来确保全局只有一个线程池实例。以下是详细的代码分析：

// 静态常量
// gdefaultnum：线程池的默认线程数，默认为 5 个线程。
// ThreadPool 类模板定义
// ThreadPool<T> 是一个模板类，接受任务类型 T，任务类型必须是可以调用的类型，例如 std::function<void()>。
// 成员变量
// _thread_num：指定线程池中的线程数。
// _threads：存储线程池中的线程对象。
// _task_queue：任务队列，存储待处理的任务。
// _isrunning：线程池是否处于运行状态，控制线程池的生命周期。
// _sleep_thread_num：记录当前处于休眠状态的线程数。
// _mutex：用于保护任务队列的互斥锁，确保线程安全。
// _cond：条件变量，用于在任务队列为空时让线程休眠，等待任务的到来。
// _tp：线程池单例对象的指针。
// _sig_mutex：用于保护线程池单例的互斥锁。
// 主要成员方法
// LockQueue() 和 UnlockQueue()
// LockQueue() 和 UnlockQueue()：分别用于加锁和解锁任务队列的互斥锁。确保对任务队列的操作是线程安全的。
// Wakeup() 和 WakeupAll()
// Wakeup()：唤醒一个等待线程。
// WakeupAll()：唤醒所有等待线程。通常用于停止线程池时，确保所有线程被唤醒。
// Sleep()
// Sleep()：让线程在条件变量上等待，并释放锁。当条件满足时，线程会被唤醒。
// IsEmpty()
// IsEmpty()：检查任务队列是否为空。
// HandlerTask(const std::string &name)
// HandlerTask()：这是工作线程的主函数。每个工作线程会不断从任务队列中取任务并执行。如果任务队列为空，线程会休眠，直到有新的任务被添加到队列中。
// 睡眠与唤醒机制：
// 如果队列为空且线程池正在运行，线程会进入休眠状态并等待条件变量的通知。
// 如果队列不为空，线程会取出任务并执行。
// Init()
// Init()：初始化线程池。通过 std::bind 将 HandlerTask 方法绑定到工作线程中，并将每个线程的任务（处理任务的方法）分配给 Thread 类实例。
// 每个线程会被命名为 thread-1, thread-2, ...，并启动。
// Start()
// Start()：启动线程池中的所有线程。通过调用每个线程对象的 Start() 方法，让它们开始执行任务。
// Stop()
// Stop()：停止线程池。设置 _isrunning 为 false，并通过 WakeupAll() 唤醒所有线程，使它们退出循环，最终结束工作。
// Equeue(const T &in)
// Equeue()：将任务 in 添加到任务队列。如果有线程在休眠，唤醒一个线程去执行任务。
// 单例模式的实现
// 单例模式保证了类只有一个实例，并提供了一个全局访问点。该实现中的 ThreadPool<T> 使用了双重检查锁定（Double-Checked Locking）来确保线程池的实例在多线程环境下安全地创建。
// _tp 和 _sig_mutex 的定义：
// _tp：这是一个静态指针，指向 ThreadPool<T> 的唯一实例。最初，它被初始化为 nullptr，表示线程池尚未被创建。
// _sig_mutex：这是一个静态互斥锁，保证在创建单例实例时，多个线程不会同时进入创建实例的代码区块，从而避免竞态条件。它通过 PTHREAD_MUTEX_INITIALIZER 进行初始化。
// GetInstance() 方法
// GetInstance() 方法实现了线程池的 单例 逻辑：
// 第一次检查（if (_tp == nullptr))：通过检查静态指针 _tp 是否为 nullptr，来判断是否已经创建了线程池。如果 nullptr，说明线程池还没有被创建。
// 加锁：当线程池尚未创建时，使用 LockGuard 对 _sig_mutex 锁进行加锁，保证在多线程环境中只有一个线程可以进入到创建线程池的代码区域。这是防止竞态条件的发生。
// 第二次检查（if (_tp == nullptr)）：由于加锁后，其他线程可能已经创建了线程池，因此需要再次检查 _tp 是否为 nullptr。这是典型的双重检查锁定（Double-Checked Locking）模式。
// 创建线程池实例：如果线程池还没有创建（_tp == nullptr），则使用 new 创建线程池实例，并调用 Init() 和 Start() 方法初始化并启动线程池。
// 返回线程池实例：最后，无论线程池是否已创建，都返回线程池实例 _tp。
// 3. 为什么要双重检查锁定？
// 双重检查锁定的目的是 提高性能。在多线程环境中：
// 第一次检查：用于避免每次都进行加锁操作。如果 _tp 已经被初始化，那么不需要再加锁，直接返回实例。这避免了每次获取单例时都需要进行锁定，减少了性能开销。
// 第二次检查：确保只有一个线程可以创建实例。即使第一个检查通过，多个线程可能会在加锁后同时尝试创建实例，因此需要再次检查 _tp 是否为 nullptr，从而确保线程池实例只会被创建一次。
// 4. 线程安全与性能
// 线程安全：通过 _sig_mutex 锁，确保了在多线程环境下，只有一个线程能创建 ThreadPool 实例，防止了多个线程同时创建实例的问题。
// 性能考虑：虽然使用了互斥锁，但在首次创建线程池时会加锁，其他时间则不需要，因此通过双重检查锁定的方式，在确保线程安全的同时，减少了不必要的加锁开销。
